<?php 

/**
 * Class Model
 * 
 * Validation example
 * <example>
 * 	if ( $model->isValid( $data ) ) {
 *		echo 'valid';
 *	}
 * </example>
 * 
 * Find, validation & save results example
 * <example>
 *  $model = new DM_Db_Model( 'users' );
 *  $data = $model->find( DM_Db_Select::create()->where( 'name LIKE "A%"' ) );
 *  $data[0]['name'] = "Alex";
 *  if ( $model->isValid( $data ) ) {
 *  		$model->saveAll( $data );
 *  }
 * </example>
 * 
 * Validation of 1 row
 * <example>
 *  $model = new DM_Db_Model( 'users' );
 *  $data = $model->find( DM_Db_Select::create()->where( 'name LIKE "A%"' ) );
 *  $data[0]['name'] = "Alex";
 *  $validators = array(
 *  	'name' => array(
 *  		'/.{5,}/' // min length 5
 *  	)
 *  ) 
 *  if ( $model->isValidRow( $data[0], $validators ) ) { // or without $validators for use model's validators
 *  	$data[0]->save(); // or $model->save( $data[0] );
 *  }
 * </example>
 */

DM_Loader::load('Db/Row');
DM_Loader::load('Db/Quote');
DM_Loader::load('Validator');

define ('DM_LINKTYPE_ONE_ONE',      'one-to-one');      // LINK
define ('DM_LINKTYPE_ONE_MANY',     'one-to-many');     // SELECT BOX
define ('DM_LINKTYPE_MANY_ONE',     'many-to-one');     // OPTIONS
define ('DM_LINKTYPE_MANY_MANY',    'many-to-many');    // MULTISELECT BOX

define ('DM_FOREIGN_CASCADE', 'cascade');
define ('DM_FOREIGN_RESTRICT', 'restrict');

class DM_Db_Model extends DM_Validator {
	var $name;
	var $primary;
        var $foreign; // use in DM_Db_Select
        /*var $foreign = [
            'usersInfo' => [
                'user_id' => [
                    'type'      => DM_LINKTYPE_ONE_ONE,
                    'delete'    => 'cascade',
                    'update'    => 'none'
                ]
            ]
        ];*/
        
	protected $db;

	public $data = null;        
	
	public function __construct( $name = null, $primary = null, &$adapter = null ) {
                parent::__construct();

		if ( !empty($name) ) {
			$this->name = $name;
		}
		
		if ( !empty($primary) ) {
			$this->primary = $primary;
		}
		
		if ( empty($adapter) ) {
			$this->db = &DM_Db_Adapter::$defaultAdapter;
		} else {
			$this->setAdapter( $adapter );
		}
	}
	
	public function setAdapter( &$adapter ) {
		if ( $adapter ) {
			$this->db = &$adapter;
		}
	}
        
        public function setForeignKeys( $keys ) {
            if ( is_array($keys) ) {
                $this->foreign = $keys;
            }
        }
	
	public function find( $sql = null, $count = 0 ) {
		if ( $sql instanceof DM_Db_Select ) {
			$sql->from( $this->name );			
		} elseif ( empty($sql) ) {
			$sql = DM_Db_Select::create()->from( $this->name );
		}
		return $this->db->fetch( $sql, $count, $this );
	}

        public function fetchAll( $sql = null, $count = 0 ) {
            return $this->find( $sql, $count );
        }

        public function fetchRow( $sql = null ) {
            if ( $sql instanceof DM_Db_Select ) {
                    $sql->from( $this->name );
            } elseif ( empty($sql) ) {
                    $sql = DM_Db_Select::create()->from( $this->name );
            }
            return $this->db->fetchRow( $sql, $this );
        }
        
        public function select() {
            return DM_Db_Select::create()
                    ->from($this)
                    ->mother($this);
        }

	public function count( $sql = null ) {
		if ( $sql instanceof DM_Db_Select ) {
                        $sqlTable = $sql->table();
                        if ( empty($sqlTable) ) {
                            $sql->from( $this->name );
                        }			
                        DM_Loader::load('Db/Expr');
			$sql->fields( array( 'rcount' => new DM_Db_Expr('COUNT(0)') ) );
		} else {			
			throw new Exception('DM_Db_Model->count :: can be work only with DM_Db_Select objects');
			return null;
		}
		$result = $this->db->fetch( $sql, 0, $this );
		return isset($result[0], $result[0]['rcount'])?$result[0]['rcount']:0;
	}
	
	protected function _implode( $symbol, $data ) {
		if ( is_array($data) ) {
			$update = array();
			foreach ($data as $key => &$item) {
				$item = DM_Db_Quote::quote( $item );
				$update[] = $key.'='.$item;
			}
			return array( 'values' => implode($symbol, $data), 'update' => implode($symbol, $update) );
		}
		return array( 'values' => '', 'update' => '' );
	}
	
	public function isValidRow( &$row, &$validate = null ) {
		$jData;
		if ( $row instanceof DM_Db_Row ) {
			$jData = $row->toArray();	
		} else {
			if ( !empty($row) && isset($row[ $this->name ]) && is_array($row[ $this->name ]) ) {
				$jData = &$row[ $this->name ];
			} else {
				$jData = &$row;
			}
		}
		
		if ( is_array($jData)) {
			return $this->valid( $jData, $validate );
		}
		return false;
	}
	
	public function isValid( &$data = null, &$validate = null ) {
		if ( is_array($data) && count($data) ) {
			$i = 0;			
			foreach( $data as &$item ) {
				if ( !$this->isValidRow( $item, $validate ) ) return false;				
			}
			return true;
		}
		return false;
	}
        
        public function update( $data, $where = null, $setModTime = true ) {
            $set = array();
            if ( count($data) ) {
                foreach( $data as $key => $value ) {
                    $set[] = DM_Db_Quote::quoteKey( $key ).' = '.DM_Db_Quote::quote( $value );
                }
            }
            
            $whereString = '1=1';
            if ( !empty($where) ) {
                if ( $where instanceof DM_Db_Select ) {
                    $whereString = $where->where;
                } elseif ( is_array($where) ) {
                    $select = DM_Db_Select::create();
                    $whereString = $select->_where( array( $where ) );
                } elseif ( is_string($where) ) {
                    $whereString = $where;
                }
            }
            
            $sql = 'UPDATE '.$this->name.' SET '.implode(',', $set). ' WHERE '.$whereString;
            
            $this->db->query( $sql );
            
            if ( $setModTime ) {
                $this->db->_setLastDataChange( $this->name );
            }
        }
	
	public function save( $data = null, $setModTime = true ) {
		$jData;
		
		if ( $data instanceof DM_Db_Row ) {
			$jData = $data->toArray();	
		} else {
			if ( !empty($data) && isset($data[ $this->name ]) && is_array($data[ $this->name ]) ) {
				$jData = &$data[ $this->name ];
			} else {
				$jData = &$data;
			}
		}
			
		if ( is_array($jData) ) {
			$imp = $this->_implode(',', $jData);
			$sql = 	'INSERT INTO '.$this->name.' ('.implode( ',',array_keys( $jData ) ).') '.
					'VALUES ('.$imp['values'].') '.
					'ON DUPLICATE KEY UPDATE '.$imp['update'];
			
			$this->db->query( $sql );
			
			if ( $setModTime ) {
                            $this->db->_setLastDataChange( $this->name );
			}
                        
                        return $this->lastInsertID();
		}
                return FALSE;
	}

	public function lastInsertID() {
		return $this->db->lastInsertID();
	}
	
	public function saveAll( &$data ) {
		if ( is_array($data) && count($data) ) {
			$this->db->_mysqli()->autocommit(FALSE);
			foreach( $data as &$item ) {
				$this->save( $item, false );
			}
			$this->db->_setLastDataChange( $this->name );
			$this->db->_mysqli()->commit();
			$this->db->_mysqli()->autocommit(TRUE);
		}
	}

	public function remove( $sql ) {
		if ( $sql instanceof DM_Db_Select ) {
			$sql->from( $this->name );
		} elseif ( empty($sql) ) {
			$sql = DM_Db_Select::create()->from( $this->name );
		}
		$this->db->query( 'DELETE FROM '.$this->name.' WHERE '.$sql->getCompiledWhere() );
	}

        public function beginTransaction() {
            $this->db->beginTransaction();
        }

        public function endTransaction() {
            $this->db->endTransaction();
        }
}

?>